#ifndef __AUTOPTR_H__
#define __AUTOPTR_H__

/*
============
A pointer template class that deletes its pointee when it is destroyed
Not intended for use in containers
An assignment causes the RHS to release its pointer, ie the pointee is only be owned by one AutoPtr at a time
============
*/
#include "PtrPolicies.h"
namespace inf {

#ifdef _DEBUG
    template< typename T, typename CleanupPolicy = DefaultCleanupPolicy< T >, typename CheckingPolicy = NULLPtrCheck< T > >
#else
    template< typename T, typename CleanupPolicy = DefaultCleanupPolicy< T >, typename CheckingPolicy = NoPtrCheck< T > >
#endif
    class AutoPtr {
    public:
        typedef CleanupPolicy cleanup_policy;
        typedef CheckingPolicy checking_policy;

        explicit AutoPtr( T* p = NULL ) : 
            pointee ( p )
#ifdef _DEBUG
            ,size( 0 )
#endif
        {
        }

        ~AutoPtr( void ) {
            cleanup_policy::free( pointee );
        }

        AutoPtr( AutoPtr< T, CleanupPolicy, CheckingPolicy >& rhs ) { 
            pointee = rhs.release();
#ifdef _DEBUG
            size = rhs.size;
#endif
        }

        AutoPtr< T, CleanupPolicy, CheckingPolicy >& operator=( AutoPtr< T, CleanupPolicy, CheckingPolicy >& rhs ) {
            if ( &rhs != this ) {
#ifdef _DEBUG
                reset( rhs.release(), rhs.size );
#else
                reset( rhs.release() );
#endif			
            }

            return (*this);
        }

        T&			operator[]( size_t index ) {
            checking_policy::check( pointee );
#ifdef _DEBUG
            if( size != 0 && ( index >= size ) ) {
                error( "Index '%u' out of range '%u'", index, size );
            }
#endif
            return get()[ index ];
        }

        const T&	operator[]( size_t index ) const {
            checking_policy::check( pointee );
#ifdef _DEBUG
            if( size != 0 && ( index >= size ) ) {
                error( "Index '%u' out of range '%u'", index, size );
            }
#endif
            return get()[ index ];
        }

        bool		valid( void ) const			{ return pointee != NULL; }

        T*			operator->()			{ checking_policy::check( pointee ); return get(); }
        const T*	operator->() const		{ checking_policy::check( pointee ); return get(); }
        T&			operator*()				{ checking_policy::check( pointee ); return *get(); }
        const T&	operator*() const		{ checking_policy::check( pointee ); return *get(); }
        T*			get( void )					{ return pointee; }
        const T*	get( void ) const				{ return pointee; }

        /*
        ============
        release
        ============
        */
        T*	release( void ) {
            T* tmp = pointee;
            pointee = NULL; 
            return tmp;
        }

        /*
        ============
        reset
        ============
        */
#ifdef _DEBUG
        void reset( T* p = NULL, size_t size = 0 ) {
            this->size = size;
#else
        void reset( T* p = NULL ) {
#endif
            if( p != pointee ) {
                cleanup_policy::free( pointee );
            }

            pointee = p;
        }

        /*
        ============
        reset
        ============
        */
        template< typename U >
#ifdef _DEBUG
        void reset( U* p = NULL, size_t size = 0 ) {
            this->size = size;
#else
        void reset( U* p = NULL ) {
#endif
            if( p != pointee ) {
                cleanup_policy::free( pointee );
            }

            pointee = p;
        }

    private:
        T* pointee;
#ifdef _DEBUG
        size_t size;
#endif
    };


}   // ::inf

#endif //__AUTOPTR_H__
